﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;

namespace SharpSoft.WPF.Controls
{
    /// <summary>
    /// 旋钮控件
    /// </summary>
    public class Knob : Slider
    {
        static Knob()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(Knob), new FrameworkPropertyMetadata(typeof(Knob)));
        }
        public Knob()
        {
        }


        /// <summary>
        /// 开始刻度与Y轴的方向角（度）
        /// </summary>
        public double StartAngle
        {
            get { return (double)GetValue(StartAngleProperty); }
            set { SetValue(StartAngleProperty, value); }
        }

        public static readonly DependencyProperty StartAngleProperty =
            DependencyProperty.Register("StartAngle", typeof(double), typeof(Knob), new PropertyMetadata(180d / 10d));


        /// <summary>
        /// 结束刻度与开始刻度的方向角（度）
        /// </summary>
        public double EndAngle
        {
            get { return (double)GetValue(EndAngleProperty); }
            set { SetValue(EndAngleProperty, value); }
        }

        public static readonly DependencyProperty EndAngleProperty =
            DependencyProperty.Register("EndAngle", typeof(double), typeof(Knob), new PropertyMetadata(180 * 1.8d));


        /// <summary>
        /// 刻度数量
        /// </summary>
        public int GraduationsCount
        {
            get { return (int)GetValue(GraduationsCountProperty); }
            set { SetValue(GraduationsCountProperty, value); }
        }

        public static readonly DependencyProperty GraduationsCountProperty =
            DependencyProperty.Register("GraduationsCount", typeof(int), typeof(Knob), new PropertyMetadata(15));



        private Size renderSize;
        private Point center;
        protected override void OnRender(DrawingContext dc)
        {
            // base.OnRender(dc);
            renderSize = new Size(this.ActualWidth, this.ActualHeight);
            center = new Point(renderSize.Width / 2, renderSize.Height / 2);

            RenderGraduations(dc);
            RenderKnob(dc);
        }

        /// <summary>
        /// 渲染刻度
        /// </summary>
        private void RenderGraduations(DrawingContext dc)
        {
            var width = renderSize.Width;
            var height = renderSize.Height;

            var pen1 = new Pen(Brushes.Blue, 1);
            var pen2 = new Pen(Brushes.Green, 1);
            dc.DrawEllipse(Brushes.Transparent, pen1, center, width / 2, height / 2);
            for (int i = 0; i <= this.GraduationsCount; i++)
            {
                var angle = this.StartAngle + this.EndAngle * (i / (double)this.GraduationsCount);
                dc.PushTransform(new RotateTransform(angle, center.X, center.Y));
                dc.DrawLine(pen1, new Point(center.X, height * 0.9), new Point(center.X, height));
                if (i != this.GraduationsCount)
                {
                    for (int j = 1; j < 10; j++)
                    {
                        dc.PushTransform(new RotateTransform((this.EndAngle / (double)this.GraduationsCount) * j / 10, center.X, center.Y));
                        dc.DrawLine(pen2, new Point(center.X, height * 0.95), new Point(center.X, height));
                        dc.Pop();
                    }
                }
                dc.Pop();
            }
        }

        private void RenderKnob(DrawingContext dc)
        {
            var width = renderSize.Width * 0.8;
            var height = renderSize.Height * 0.8;

            var scale = (this.Value - this.Minimum) / (this.Maximum - this.Minimum);
            var angle = this.StartAngle + this.EndAngle * scale;

            dc.PushTransform(new RotateTransform(angle, center.X, center.Y));

            var pen = new Pen(Brushes.Red, 1);
            dc.DrawEllipse(Brushes.Transparent, pen, center, width / 2, height / 2); 
            dc.DrawLine(pen, new Point(center.X, (center.Y + height / 2) * 0.88), new Point(center.X, center.Y + height / 2));
            dc.DrawEllipse(Brushes.Red, pen, new Point(center.X, center.Y + height / 2), width / 50, height / 50);
        }

        Point lastpos;
        bool rotate = false;
        protected override void OnPreviewMouseDown(MouseButtonEventArgs e)
        {
            base.OnPreviewMouseDown(e);
            lastpos = e.GetPosition(this);
            rotate = true;
        }
        protected override void OnPreviewMouseMove(MouseEventArgs e)
        {
            base.OnPreviewMouseMove(e);
            if (!rotate)
            {
                return;
            }
            var p = e.GetPosition(this);
            double angle = Angle(center, lastpos, p);


            var offset = (this.Maximum - this.Minimum) * (angle / this.EndAngle);

            var v = this.Value + offset;
            SetValue(v);

            lastpos = p;
        }

        private void SetValue(double v)
        {
            if (v < this.Minimum)
            {
                this.Value = this.Minimum;
            }
            else if (v > this.Maximum)
            {
                this.Value = this.Maximum;
            }
            else
            {
                this.Value = v;
            }
        }
        /// <summary>
        /// 获取两点之间的距离
        /// </summary>
        /// <param name="p1"></param>
        /// <param name="p2"></param>
        /// <returns></returns>
        private double getdis(Point p1, Point p2)
        {
            return Math.Sqrt(Math.Pow(p2.X - p1.X, 2) + Math.Pow(p2.Y - p1.Y, 2));
        }
        public static double Angle(Point cen, Point first, Point second)
        {
            const double M_PI = Math.PI;

            double ma_x = first.X - cen.X;
            double ma_y = first.Y - cen.Y;
            double mb_x = second.X - cen.X;
            double mb_y = second.Y - cen.Y;

            var afirst = (Math.Atan2(ma_y, ma_x));

            var asecond = (Math.Atan2(mb_y, mb_x));

            return 180 * optimizeRadian(asecond - afirst) / M_PI;
        }

        private static double optimizeRadian(double radian)
        {
            var pi = Math.PI;
            if (radian > pi)
            {
                radian = pi * 2 - radian;
            }
            else if (radian < -pi)
            {
                radian = radian + pi * 2;
            }
            else
            {
                return radian;
            }
            radian = optimizeRadian(radian);
            return radian;

        }

        protected override void OnMouseLeave(MouseEventArgs e)
        {
            rotate = false;
            base.OnMouseLeave(e);
        }

        protected override void OnPreviewMouseUp(MouseButtonEventArgs e)
        {
            base.OnPreviewMouseUp(e);
            rotate = false;
        }

        protected override void OnPreviewMouseWheel(MouseWheelEventArgs e)
        {
            base.OnPreviewMouseWheel(e);
            var unit = (this.Maximum - this.Minimum) / (double)this.GraduationsCount;
            SetValue(this.Value + unit * e.Delta / 120);
        }

        protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
        {
            base.OnPropertyChanged(e);
            if (e.Property == ValueProperty
                || e.Property == MaximumProperty || e.Property == MinimumProperty
                || e.Property == StartAngleProperty || e.Property == EndAngleProperty
                || e.Property == GraduationsCountProperty)
            {
                this.InvalidateVisual();
            }
            else if (e.Property == HeightProperty)
            { 
            }
        }

    }
}
